use crate::vm::addr::paddr::Paddr;

use super::{
    common::{memblock_cap_size, memblock_isolate_range, memblock_merge_regions},
    Memblock, MemblockFlags, MemblockRegion, MemblockType,
};

#[inline]
fn memblock_set_region_flags(r: &mut MemblockRegion, flag: MemblockFlags) {
    r.flags |= flag;
}

#[inline]
fn memblock_clear_region_flags(r: &mut MemblockRegion, flag: MemblockFlags) {
    r.flags &= !flag;
}

fn memblock_setclr_flag(
    base: Paddr,
    size: usize,
    set: bool,
    flag: MemblockFlags,
    memblock: &mut Memblock,
) -> Result<(), ()> {
    let mut start_idx = 0;
    let mut end_idx = 0;

    let mtype = &mut memblock.memory;
    memblock_isolate_range(mtype, base, size, &mut start_idx, &mut end_idx)?;

    for i in start_idx..end_idx {
        if set {
            memblock_set_region_flags(&mut mtype.regions[i as usize], flag);
        } else {
            memblock_clear_region_flags(&mut mtype.regions[i as usize], flag);
        }
    }

    memblock_merge_regions(mtype);

    Ok(())
}

impl Memblock {
    pub fn mark_dma(&mut self, base: Paddr, size: usize) -> Result<(), ()> {
        memblock_setclr_flag(base, size, true, MemblockFlags::MEMBLOCK_DMA, self)
    }

    pub fn clear_dma(&mut self, base: Paddr, size: usize) -> Result<(), ()> {
        memblock_setclr_flag(base, size, false, MemblockFlags::MEMBLOCK_DMA, self)
    }
}

impl MemblockRegion {
    pub fn is_dma(&self) -> bool {
        self.flags & MemblockFlags::MEMBLOCK_DMA == MemblockFlags::MEMBLOCK_DMA
    }
}

fn memblock_search(ty: &MemblockType, addr: Paddr) -> i32 {
    let mut left: i32 = 0;
    let mut right: i32 = ty.cnt as i32;

    loop {
        let mid = (right + left) / 2;

        if addr < ty.regions[mid as usize].base {
            right = mid;
        } else if addr >= (ty.regions[mid as usize].base + ty.regions[mid as usize].size) {
            left = mid + 1;
        } else {
            return mid;
        }
        if left >= right {
            break;
        }
    }
    -1
}

impl Memblock {
    /// 判断当前地址是否是可用内存区域 (通过 memblock_add 添加)
    pub fn memblock_is_memory(&self, addr: Paddr) -> bool {
        memblock_search(&self.memory, addr) != -1
    }

    /// 判断当前地址是否是预留内存区域 (通过 memblock_reserve 添加)
    pub fn memblock_is_reserved(&self, addr: Paddr) -> bool {
        memblock_search(&self.reserved, addr) != -1
    }

    /// 判断当前地址范围是否是可用内存区域 (通过 memblock_add 添加)
    pub fn memblock_is_region_memory(&self, base: Paddr, size: usize) -> bool {
        let i = memblock_search(&self.memory, base);
        if i == -1 {
            return false;
        }
        let rgn = self.memory.regions[i as usize];
        let mut sz = size;
        rgn.base + rgn.size >= base + memblock_cap_size(base, &mut sz)
    }
}

fn memblock_addrs_overlap(base1: Paddr, size1: usize, base2: Paddr, size2: usize) -> bool {
    (base1 < (base2 + size2)) && (base2 < (base1 + size1))
}

fn memblock_overlaps_region(ty: &MemblockType, base: Paddr, size: usize) -> bool {
    for r in ty.regions.iter().take(ty.cnt) {
        if memblock_addrs_overlap(base, size, r.base, r.size) {
            return true;
        }
    }
    false
}

impl Memblock {
    /// 判断当前地址范围是否是预留内存区域 (通过 memblock_reserve 添加)
    pub fn memblock_is_region_reserved(&self, base: Paddr, mut size: usize) -> bool {
        memblock_cap_size(base, &mut size);
        memblock_overlaps_region(&self.reserved, base, size)
    }
}
